package licode.unisop.client.service.filter;

import licode.unisop.client.api.SopPlatform;
import licode.unisop.client.api.SopPlatformAuth;
import licode.unisop.client.api.SopUriFilter;
import licode.unisop.client.api.auth.SopUserAuthQuery;
import licode.unisop.client.info.SopIdInfo;
import licode.unisop.client.info.SopTokenInfo;
import licode.unisop.client.service.impl.SopAuthCheck;
import licode.unisop.client.utils.*;
import licode.unisop.client.vo.SopAuthPlatform;
import licode.unisop.client.vo.SopUrl;
import licode.unisop.client.vo.SopUrlMatch;
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.http.MimeHeaders;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.List;

/**
 * @author licode
 */
@Slf4j
@Component
public class SopAuthorizationHandler {
    private final AntPathMatcher pathMatcher = new AntPathMatcher();

    @Resource
    private SopUriUtil sopUriUtil;

    @Resource
    private SopAuthCheck sopAuthCheck;

    @Resource
    private SopPlatformAuth sopPlatformAuth;

    @Resource
    private SopUserAuthQuery sopUserAuthQuery;

    @Value("${uni-sop.no-filter:false}")
    private boolean noFilter;

    @Value("${uni-sop.auto-renew:true}")
    private boolean autoRenew;

    private SopPlatform initPlatformToken(HttpServletRequest request, String platId) {
        SopPlatform platform = sopAuthCheck.getPlatformById(platId);
        if (null != platform) {
            reflectSetHeader(request, SopAuthConst.PLATFORM_TOKEN, platform.getId());
        }

        return platform;
    }

    private boolean isLogin(SopTokenInfo sopTokenInfo) {
        return autoRenew ? sopUserAuthQuery.getCredential(sopTokenInfo) != null
                : sopUserAuthQuery.tokenValid(sopTokenInfo);
    }

    private SopUrlMatch executeMatch(String url, SopTokenInfo sopTokenInfo, List<SopUrl> urls) {
        if (null != urls && !urls.isEmpty()) {
            SopUrl sopUrl = null;
            for (SopUrl pattern : urls) {
                if (pathMatcher.match(pattern.getUrl(), url)) {
                    sopUrl = pattern;
                }
            }
            if (sopUrl != null) {
                if (sopUrl.getType() == 1) {
                    return SopUrlMatch.ok(false);
                } else if (sopUrl.getType() == 2) {
                    return SopUrlMatch.disabled(false);
                }
            }
        }

        if (isLogin(sopTokenInfo)) {
            return SopUrlMatch.noMatch(true);
        } else {
            return SopUrlMatch.noMatch(false);
        }
    }

    public SopException doHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
        // 获取进过拦截器的路径
        SopUriFilter uriFilter;

        if (noFilter) {
            return null;
        }

        // 获取请求URI
        String url = preHandle(request);
        String platId = sopAuthCheck.getPlatformId(request);

        if (StringUtil.isBlank(url)) {
            initPlatformToken(request, platId);
            return null;
        }

        SopPlatform platform = initPlatformToken(request, platId);
        SopAuthPlatform authPlat;

        if (null != platform) {
            authPlat = platform.getAuthPlatform();
        } else {
            if (StringUtil.isNotBlank(platId)) {
                authPlat = sopPlatformAuth.fromId(SopIdInfo.builder().id(platId).build());
            } else {
                authPlat = null;
            }
        }

        if (null == authPlat || !checkPlatform(response, authPlat)) {
            return null;
        }

        if (null != platform) {
            // 检查平台的URL过滤器插件
            uriFilter = platform.getUriFilter();
            if (null != uriFilter) {
                if (uriFilter.ignore(url)) {
                    return null;
                } else if (uriFilter.forbidden(url)) {
                    return new SopException(401,  "url(" + url + ") forbidden");
                }
            }
        }

        // 处理平台配置的过滤策略
        SopTokenInfo sopTokenInfo = SopRequestParse.getAccessTokenInfo(request);
        sopTokenInfo.setPlatformId(authPlat.getPlatformId());
        SopUrlMatch urlMatch = executeMatch(url, sopTokenInfo, authPlat.getUrls());

        if (urlMatch.isOk()) {
            return null;
        } else if (urlMatch.isDisabled()) {
            return new SopException(SopErrorCode.NO_GRANT_ACCESS_URL,
                    SopErrorCode.NO_GRANT_ACCESS_URL_MSG);
        } else if (authPlat.configAsBoolean(SopAuthPlatform.FILTER_URL, true)) {
            // 如果平台配置了需要对访问的地址进行过滤处理，则返回true
            if (!urlMatch.isLogin()) {
                // 检测是否密码或隐式登录
                log.info("用户未登录: {}", url);
                return new SopException(SopErrorCode.USER_NOT_LOGIN,
                        SopErrorCode.USER_NOT_LOGIN_MSG);
            }
        }

        return null;
    }

    private void reflectSetHeader(HttpServletRequest request, String key, String value) {
        Class<? extends HttpServletRequest> requestClass = request.getClass();
        try {
            Field requestField = requestClass.getDeclaredField("request");
            requestField.setAccessible(true);
            Object requestObj = requestField.get(request);
            Field coyoteRequestField = requestObj.getClass().getDeclaredField("coyoteRequest");
            coyoteRequestField.setAccessible(true);
            Object coyoteRequestObj = coyoteRequestField.get(requestObj);
            Field headersField = coyoteRequestObj.getClass().getDeclaredField("headers");
            headersField.setAccessible(true);
            MimeHeaders headersObj = (MimeHeaders) headersField.get(coyoteRequestObj);
            headersObj.removeHeader(key);
            headersObj.addValue(key).setString(value);
        } catch (Exception e) {
            log.error("reflect set header {} error {}", key, e);
        }
    }

    private String preHandle(HttpServletRequest request) {
        // 获取请求URI
        String url = request.getRequestURI();

        if (sopUriUtil.isFilter(url)) {
            return "";
        }

        String ssoIn = SopRequestParse.getValue(request, "sso_in");
        if (StringUtil.isNotBlank(ssoIn)) {
            return "";
        }

        return url;
    }

    private boolean checkPlatform(HttpServletResponse response,
                                  SopAuthPlatform platform) throws IOException {
        if (null == platform || platform.getToken() == null) {
            throw new SopException(SopErrorCode.PLATFORM_NOT_AUTH,
                    SopErrorCode.PLATFORM_NOT_AUTH_MSG);
        }
        return true;
    }
}
